1 /*
2 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.security.auth;
27
28 import java.util.*;
29 import java.text.MessageFormat;
30 import java.security.Permission;
31 import java.security.PermissionCollection;
32 import java.security.Principal;
33 import sun.security.util.ResourcesMgr;
34
35 /**
36 * This class is used to protect access to private Credentials
37 * belonging to a particular <code>Subject</code>. The <code>Subject</code>
38 * is represented by a Set of Principals.
39 *
40 * <p> The target name of this <code>Permission</code> specifies
41 * a Credential class name, and a Set of Principals.
42 * The only valid value for this Permission's actions is, "read".
43 * The target name must abide by the following syntax:
44 *
45 * <pre>
46 * CredentialClass {PrincipalClass "PrincipalName"}*
47 * </pre>
48 *
49 * For example, the following permission grants access to the
50 * com.sun.PrivateCredential owned by Subjects which have
51 * a com.sun.Principal with the name, "duke". Note that although
52 * this example, as well as all the examples below, do not contain
53 * Codebase, SignedBy, or Principal information in the grant statement
54 * (for simplicity reasons), actual policy configurations should
55 * specify that information when appropriate.
56 *
57 * <pre>
58 *
59 * grant {
60 * permission javax.security.auth.PrivateCredentialPermission
61 * "com.sun.PrivateCredential com.sun.Principal \"duke\"",
62 * "read";
63 * };
64 * </pre>
65 *
66 * If CredentialClass is "*", then access is granted to
67 * all private Credentials belonging to the specified
68 * <code>Subject</code>.
69 * If "PrincipalName" is "*", then access is granted to the
70 * specified Credential owned by any <code>Subject</code> that has the
71 * specified <code>Principal</code> (the actual PrincipalName doesn't matter).
72 * For example, the following grants access to the
73 * a.b.Credential owned by any <code>Subject</code> that has
74 * an a.b.Principal.
75 *
76 * <pre>
77 * grant {
78 * permission javax.security.auth.PrivateCredentialPermission
79 * "a.b.Credential a.b.Principal "*"",
80 * "read";
81 * };
82 * </pre>
83 *
84 * If both the PrincipalClass and "PrincipalName" are "*",
85 * then access is granted to the specified Credential owned by
86 * any <code>Subject</code>.
87 *
88 * <p> In addition, the PrincipalClass/PrincipalName pairing may be repeated:
89 *
90 * <pre>
91 * grant {
92 * permission javax.security.auth.PrivateCredentialPermission
93 * "a.b.Credential a.b.Principal "duke" c.d.Principal "dukette"",
94 * "read";
95 * };
96 * </pre>
97 *
98 * The above grants access to the private Credential, "a.b.Credential",
99 * belonging to a <code>Subject</code> with at least two associated Principals:
100 * "a.b.Principal" with the name, "duke", and "c.d.Principal", with the name,
101 * "dukette".
102 *
103 */
104 public final class PrivateCredentialPermission extends Permission {
105
106 private static final long serialVersionUID = 5284372143517237068L;
107
108 private static final CredOwner[] EMPTY_PRINCIPALS = new CredOwner[0];
109
110 /**
111 * @serial
112 */
113 private String credentialClass;
114
115 /**
116 * @serial The Principals associated with this permission.
117 * The set contains elements of type,
118 * <code>PrivateCredentialPermission.CredOwner</code>.
119 */
120 private Set principals; // ignored - kept around for compatibility
121 private transient CredOwner[] credOwners;
122
123 /**
124 * @serial
125 */
126 private boolean testing = false;
127
128 /**
129 * Create a new <code>PrivateCredentialPermission</code>
130 * with the specified <code>credentialClass</code> and Principals.
131 */
132 PrivateCredentialPermission(String credentialClass,
133 Set<Principal> principals) {
134
135 super(credentialClass);
136 this.credentialClass = credentialClass;
137
138 synchronized(principals) {
139 if (principals.size() == 0) {
140 this.credOwners = EMPTY_PRINCIPALS;
141 } else {
142 this.credOwners = new CredOwner[principals.size()];
143 int index = 0;
144 Iterator<Principal> i = principals.iterator();
145 while (i.hasNext()) {
146 Principal p = i.next();
147 this.credOwners[index++] = new CredOwner
148 (p.getClass().getName(),
149 p.getName());
150 }
151 }
152 }
153 }
154
155 /**
156 * Creates a new <code>PrivateCredentialPermission</code>
157 * with the specified <code>name</code>. The <code>name</code>
158 * specifies both a Credential class and a <code>Principal</code> Set.
159 *
160 * <p>
161 *
162 * @param name the name specifying the Credential class and
163 * <code>Principal</code> Set. <p>
164 *
165 * @param actions the actions specifying that the Credential can be read.
166 *
167 * @throws IllegalArgumentException if <code>name</code> does not conform
168 * to the correct syntax or if <code>actions</code> is not "read".
169 */
170 public PrivateCredentialPermission(String name, String actions) {
171 super(name);
172
173 if (!"read".equalsIgnoreCase(actions))
174 throw new IllegalArgumentException
175 (ResourcesMgr.getString("actions.can.only.be.read."));
176 init(name);
177 }
178
179 /**
180 * Returns the Class name of the Credential associated with this
181 * <code>PrivateCredentialPermission</code>.
182 *
183 * <p>
184 *
185 * @return the Class name of the Credential associated with this
186 * <code>PrivateCredentialPermission</code>.
187 */
188 public String getCredentialClass() {
189 return credentialClass;
190 }
191
192 /**
193 * Returns the <code>Principal</code> classes and names
194 * associated with this <code>PrivateCredentialPermission</code>.
195 * The information is returned as a two-dimensional array (array[x][y]).
196 * The 'x' value corresponds to the number of <code>Principal</code>
197 * class and name pairs. When (y==0), it corresponds to
198 * the <code>Principal</code> class value, and when (y==1),
199 * it corresponds to the <code>Principal</code> name value.
200 * For example, array[0][0] corresponds to the class name of
201 * the first <code>Principal</code> in the array. array[0][1]
202 * corresponds to the <code>Principal</code> name of the
203 * first <code>Principal</code> in the array.
204 *
205 * <p>
206 *
207 * @return the <code>Principal</code> class and names associated
208 * with this <code>PrivateCredentialPermission</code>.
209 */
210 public String[][] getPrincipals() {
211
212 if (credOwners == null || credOwners.length == 0) {
213 return new String[0][0];
214 }
215
216 String[][] pArray = new String[credOwners.length][2];
217 for (int i = 0; i < credOwners.length; i++) {
218 pArray[i][0] = credOwners[i].principalClass;
219 pArray[i][1] = credOwners[i].principalName;
220 }
221 return pArray;
222 }
223
224 /**
225 * Checks if this <code>PrivateCredentialPermission</code> implies
226 * the specified <code>Permission</code>.
227 *
228 * <p>
229 *
230 * This method returns true if:
231 * <p><ul>
232 * <li> <i>p</i> is an instanceof PrivateCredentialPermission and <p>
233 * <li> the target name for <i>p</i> is implied by this object's
234 * target name. For example:
235 * <pre>
236 * [* P1 "duke"] implies [a.b.Credential P1 "duke"].
237 * [C1 P1 "duke"] implies [C1 P1 "duke" P2 "dukette"].
238 * [C1 P2 "dukette"] implies [C1 P1 "duke" P2 "dukette"].
239 * </pre>
240 * </ul>
241 *
242 * <p>
243 *
244 * @param p the <code>Permission</code> to check against.
245 *
246 * @return true if this <code>PrivateCredentialPermission</code> implies
247 * the specified <code>Permission</code>, false if not.
248 */
249 public boolean implies(Permission p) {
250
251 if (p == null || !(p instanceof PrivateCredentialPermission))
252 return false;
253
254 PrivateCredentialPermission that = (PrivateCredentialPermission)p;
255
256 if (!impliesCredentialClass(credentialClass, that.credentialClass))
257 return false;
258
259 return impliesPrincipalSet(credOwners, that.credOwners);
260 }
261
262 /**
263 * Checks two <code>PrivateCredentialPermission</code> objects for
264 * equality. Checks that <i>obj</i> is a
265 * <code>PrivateCredentialPermission</code>,
266 * and has the same credential class as this object,
267 * as well as the same Principals as this object.
268 * The order of the Principals in the respective Permission's
269 * target names is not relevant.
270 *
271 * <p>
272 *
273 * @param obj the object we are testing for equality with this object.
274 *
275 * @return true if obj is a <code>PrivateCredentialPermission</code>,
276 * has the same credential class as this object,
277 * and has the same Principals as this object.
278 */
279 public boolean equals(Object obj) {
280 if (obj == this)
281 return true;
282
283 if (! (obj instanceof PrivateCredentialPermission))
284 return false;
285
286 PrivateCredentialPermission that = (PrivateCredentialPermission)obj;
287
288 return (this.implies(that) && that.implies(this));
289 }
290
291 /**
292 * Returns the hash code value for this object.
293 *
294 * @return a hash code value for this object.
295 */
296 public int hashCode() {
297 return this.credentialClass.hashCode();
298 }
299
300 /**
301 * Returns the "canonical string representation" of the actions.
302 * This method always returns the String, "read".
303 *
304 * <p>
305 *
306 * @return the actions (always returns "read").
307 */
308 public String getActions() {
309 return "read";
310 }
311
312 /**
313 * Return a homogeneous collection of PrivateCredentialPermissions
314 * in a <code>PermissionCollection</code>.
315 * No such <code>PermissionCollection</code> is defined,
316 * so this method always returns <code>null</code>.
317 *
318 * <p>
319 *
320 * @return null in all cases.
321 */
322 public PermissionCollection newPermissionCollection() {
323 return null;
324 }
325
326 private void init(String name) {
327
328 if (name == null || name.trim().length() == 0) {
329 throw new IllegalArgumentException("invalid empty name");
330 }
331
332 ArrayList<CredOwner> pList = new ArrayList<>();
333 StringTokenizer tokenizer = new StringTokenizer(name, " ", true);
334 String principalClass = null;
335 String principalName = null;
336
337 if (testing)
338 System.out.println("whole name = " + name);
339
340 // get the Credential Class
341 credentialClass = tokenizer.nextToken();
342 if (testing)
343 System.out.println("Credential Class = " + credentialClass);
344
345 if (tokenizer.hasMoreTokens() == false) {
346 MessageFormat form = new MessageFormat(ResourcesMgr.getString
347 ("permission.name.name.syntax.invalid."));
348 Object[] source = {name};
349 throw new IllegalArgumentException
350 (form.format(source) + ResourcesMgr.getString
351 ("Credential.Class.not.followed.by.a.Principal.Class.and.Name"));
352 }
353
354 while (tokenizer.hasMoreTokens()) {
355
356 // skip delimiter
357 tokenizer.nextToken();
358
359 // get the Principal Class
360 principalClass = tokenizer.nextToken();
361 if (testing)
362 System.out.println(" Principal Class = " + principalClass);
363
364 if (tokenizer.hasMoreTokens() == false) {
365 MessageFormat form = new MessageFormat(ResourcesMgr.getString
366 ("permission.name.name.syntax.invalid."));
367 Object[] source = {name};
368 throw new IllegalArgumentException
369 (form.format(source) + ResourcesMgr.getString
370 ("Principal.Class.not.followed.by.a.Principal.Name"));
371 }
372
373 // skip delimiter
374 tokenizer.nextToken();
375
376 // get the Principal Name
377 principalName = tokenizer.nextToken();
378
379 if (!principalName.startsWith("\"")) {
380 MessageFormat form = new MessageFormat(ResourcesMgr.getString
381 ("permission.name.name.syntax.invalid."));
382 Object[] source = {name};
383 throw new IllegalArgumentException
384 (form.format(source) + ResourcesMgr.getString
385 ("Principal.Name.must.be.surrounded.by.quotes"));
386 }
387
388 if (!principalName.endsWith("\"")) {
389
390 // we have a name with spaces in it --
391 // keep parsing until we find the end quote,
392 // and keep the spaces in the name
393
394 while (tokenizer.hasMoreTokens()) {
395 principalName = principalName + tokenizer.nextToken();
396 if (principalName.endsWith("\""))
397 break;
398 }
399
400 if (!principalName.endsWith("\"")) {
401 MessageFormat form = new MessageFormat
402 (ResourcesMgr.getString
403 ("permission.name.name.syntax.invalid."));
404 Object[] source = {name};
405 throw new IllegalArgumentException
406 (form.format(source) + ResourcesMgr.getString
407 ("Principal.Name.missing.end.quote"));
408 }
409 }
410
411 if (testing)
412 System.out.println("\tprincipalName = '" + principalName + "'");
413
414 principalName = principalName.substring
415 (1, principalName.length() - 1);
416
417 if (principalClass.equals("*") &&
418 !principalName.equals("*")) {
419 throw new IllegalArgumentException(ResourcesMgr.getString
420 ("PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value"));
421 }
422
423 if (testing)
424 System.out.println("\tprincipalName = '" + principalName + "'");
425
426 pList.add(new CredOwner(principalClass, principalName));
427 }
428
429 this.credOwners = new CredOwner[pList.size()];
430 pList.toArray(this.credOwners);
431 }
432
433 private boolean impliesCredentialClass(String thisC, String thatC) {
434
435 // this should never happen
436 if (thisC == null || thatC == null)
437 return false;
438
439 if (testing)
440 System.out.println("credential class comparison: " +
441 thisC + "/" + thatC);
442
443 if (thisC.equals("*"))
444 return true;
445
446 /**
447 * XXX let's not enable this for now --
448 * if people want it, we'll enable it later
449 */
450 /*
451 if (thisC.endsWith("*")) {
452 String cClass = thisC.substring(0, thisC.length() - 2);
453 return thatC.startsWith(cClass);
454 }
455 */
456
457 return thisC.equals(thatC);
458 }
459
460 private boolean impliesPrincipalSet(CredOwner[] thisP, CredOwner[] thatP) {
461
462 // this should never happen
463 if (thisP == null || thatP == null)
464 return false;
465
466 if (thatP.length == 0)
467 return true;
468
469 if (thisP.length == 0)
470 return false;
471
472 for (int i = 0; i < thisP.length; i++) {
473 boolean foundMatch = false;
474 for (int j = 0; j < thatP.length; j++) {
475 if (thisP[i].implies(thatP[j])) {
476 foundMatch = true;
477 break;
478 }
479 }
480 if (!foundMatch) {
481 return false;
482 }
483 }
484 return true;
485 }
486
487 /**
488 * Reads this object from a stream (i.e., deserializes it)
489 */
490 private void readObject(java.io.ObjectInputStream s) throws
491 java.io.IOException,
492 ClassNotFoundException {
493
494 s.defaultReadObject();
495
496 // perform new initialization from the permission name
497
498 if (getName().indexOf(" ") == -1 && getName().indexOf("\"") == -1) {
499
500 // name only has a credential class specified
501 credentialClass = getName();
502 credOwners = EMPTY_PRINCIPALS;
503
504 } else {
505
506 // perform regular initialization
507 init(getName());
508 }
509 }
510
511 /**
512 * @serial include
513 */
514 static class CredOwner implements java.io.Serializable {
515
516 private static final long serialVersionUID = -5607449830436408266L;
517
518 /**
519 * @serial
520 */
521 String principalClass;
522 /**
523 * @serial
524 */
525 String principalName;
526
527 CredOwner(String principalClass, String principalName) {
528 this.principalClass = principalClass;
529 this.principalName = principalName;
530 }
531
532 public boolean implies(Object obj) {
533 if (obj == null || !(obj instanceof CredOwner))
534 return false;
535
536 CredOwner that = (CredOwner)obj;
537
538 if (principalClass.equals("*") ||
539 principalClass.equals(that.principalClass)) {
540
541 if (principalName.equals("*") ||
542 principalName.equals(that.principalName)) {
543 return true;
544 }
545 }
546
547 /**
548 * XXX no code yet to support a.b.*
549 */
550
551 return false;
552 }
553
554 public String toString() {
555 MessageFormat form = new MessageFormat(ResourcesMgr.getString
556 ("CredOwner.Principal.Class.class.Principal.Name.name"));
557 Object[] source = {principalClass, principalName};
558 return (form.format(source));
559 }
560 }
561 }